#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include "Task.hpp"
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Daemon.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10;

extern Log lg;

enum{
    SockError=1,
    BindError,
    ListenError,
    UsageError
};

class TcpServer;

struct ThreadData
{
    ThreadData(int sockfd, const std::string& clientip, uint16_t clientport, TcpServer* tsvr)
    : sockfd_(sockfd), clientip_(clientip), clientport_(clientport), tsvr_(tsvr)
    {}

    int sockfd_;
    std::string clientip_;
    uint16_t clientport_;
    TcpServer* tsvr_;
};

class TcpServer
{
public:
    TcpServer(const uint16_t& port, const std::string& ip = defaultip) : listensock_(defaultfd),ip_(ip), port_(port)
    {}

    void InitServer()
    {
        listensock_ = socket(AF_INET,SOCK_STREAM, 0);
        if(listensock_ < 0)
        {
            lg(Fatal, "create socket error, errno : %d, errstring : %s", errno, strerror(errno));
            exit(SockError);
        }
        lg(Info, "create socket success, listensock : %d", listensock_);

        int opt = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));
        
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        if(bind(listensock_, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno : %d, errorstring : %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind socket success, listensock : %d", listensock_);

        if(listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno : %d, errorstring : %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "listen socket success, listensock : %d", listensock_);

    }

    static void* Routine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        td->tsvr_->Service(td->sockfd_, td->clientip_, td->clientport_);
        delete td;
        return nullptr;
    }

    void Start()
    {
        Daemon();
        ThreadPool<Task>::GetInstance()->Start();
        lg(Info, "Server is running...");
        for(;;)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                lg(Warning, "accept error, errno : %d, errorstring : %s", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            lg(Info, "get a new link..., sockfd : %d, client ip : %s, client port : %d", sockfd, clientip, clientport);
            // version 1 单进程
            // Service(sockfd, clientip, clientport);
            // close(sockfd);

            // version 2 多进程
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     close(listensock_);
            //     if(fork() > 0) exit(0);
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);
            // pid_t rid = waitpid(id, nullptr, 0);
            // (void)rid;

            // version 3 多线程
            // ThreadData* td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            // version 4 线程池
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

    void Service(int sockfd, const std::string& clientip, uint16_t clientport)
    {
        char buffer[4096];
        while(true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say#" << buffer << std::endl;
                std::string echo_string = "tcpserver echo#";
                echo_string += buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if(n == 0)
            {
                lg(Info, "%s:%d quit, server close sockfd:%d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                lg(Warning, "read error,sockfd : %d, client ip : %s, client port : %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }

    ~TcpServer()
    {
        if(listensock_ > 0) close(listensock_);
    }
private:
    int listensock_;
    std::string ip_;
    uint16_t port_;
};